fix(daemon): reset turnCompletedCleanly on same-run retry teardown - #5341
Conversation
A same-run retry reuses one run object across attempts. tearDownAttemptForRetry cleared the per-attempt fields (child, exitCode, signal, error, stdinOpen) before re-spawning but left run.turnCompletedCleanly untouched. That flag is set by a clean `turn_end` (applyClaudeStreamJsonRunBookkeeping) and read by the terminal classifier: when attempt 1 finished a clean-but-empty turn (retryable no-output timeout) and attempt 2 then crashed (exit 1, no output), the crashed run was classified 'succeeded' off attempt 1's stale flag β and reported retry_result: success in telemetry. Clear the flag in teardown alongside the other per-attempt resets, with an invariant comment so future per-attempt state that feeds terminal classification is cleared in the same place. Regression: tests/retry-stale-turn-completed-flag.test.ts drives the full run lifecycle over the daemon HTTP API with a fake claude CLI (empty turn then hang, crash on retry). Red on main, green with this fix; deterministic 8/8 both ways.
|
π§ͺ Queued for QA validation β this PR touches a run-lifecycle path that needs a manual QA pass before merge. Nothing needed from you right now; we'll update here once itβs validated. Thanks for the thorough repro + verification notes. |
nettee
left a comment
There was a problem hiding this comment.
@tomsen-ai I reviewed the retry teardown change in apps/daemon/src/server.ts and the new end-to-end regression in apps/daemon/tests/retry-stale-turn-completed-flag.test.ts. The reset in tearDownAttemptForRetry() matches the close-classification path that still reads run.turnCompletedCleanly, and the new test exercises the exact stale-flag sequence that was misclassifying a crashed retry as success. I couldnβt rerun the daemon Vitest target in this reviewer worktree because dependencies are not installed here (vitest/node_modules missing), but the code path and test coverage both look coherent. Nice catch and a tight regression harness.
|
QA validation passed for head Local targeted validation:
Remote PR checks were refreshed and the relevant CI jobs are green. I also checked Marking this runtime validation as passed. |
AmyShang-alt
left a comment
There was a problem hiding this comment.
QA validation passed on head a0f2896; approving.
π π‘ You just leveled up to Giotto
π Your contributions are sending a clear signal across the network: you care about making Open Design better. Keep transmitting. π Thanks for helping Open Design move forward. Keep building in the open. π π Rank #56 among 400+ contributors |

Fixes #5340
Why
I'm hardening the daemon's run-lifecycle / event-retention path and hit this while auditing the same-run retry teardown.
run.turnCompletedCleanlyis a run-scoped success override, but the teardown that recycles therunobject between attempts doesn't clear it β so a clean-but-empty attempt 1 silently vouches for a crashed attempt 2. The pain: a genuinely failed run is reportedsucceeded(blank result shown as success), andrun_retry_finishedreportsretry_result: success, which also skews retry telemetry.This is the same flag as #4197 (set too early) and #5159 (set when it shouldn't be), but a distinct root cause β the flag being carried forward across a retry. Neither of those touches
tearDownAttemptForRetry.What users will see
A run whose retried attempt crashes with no output now correctly shows as failed instead of a blank "success". No new surface; behavior correction only.
Surface area
Bug fix verification
apps/daemon/tests/retry-stale-turn-completed-flag.test.tsβ drives the full run lifecycle over the daemon HTTP API with a fakeclaudeCLI (clean-but-empty turn β hang β watchdog retry β crash).mainand green on this branch? yes β deterministic 8/8 red onmain, 8/8 green with the fix (the watchdog window is set wide enough that attempt 1'sturn_endis always processed first, so there's no flaky false-green).succeededbefore the fix, 5/5failedafter β the one-line teardown reset is the only variable that flips it.Validation
pnpm guard(78 pass) +pnpm --filter @open-design/daemon exec tsc -p tsconfig.json --noEmit(clean)pnpm --filter @open-design/daemon testfor the touched area:retry-stale-turn-completed-flag,run-retry-policy,run-retry-runtime,run-failure-classification,run-lifecycle-tracer,daemon-lifecycle,chat-run-artifact-quiet-period,headless-runsβ all green (133+ tests)